/*
	Copyright (c) 2008 TPSi
	Alex Iskander
	
	Permission is hereby granted, free of charge, to any person obtaining a copy
	of this software and associated documentation files (the "Software"), to deal
	in the Software without restriction, including without limitation the rights
	to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
	copies of the Software, and to permit persons to whom the Software is
	furnished to do so, subject to the following conditions:

	The above copyright notice and this permission notice shall be included in
	all copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
	OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
	THE SOFTWARE.

*/

#ifndef BINDABLE_BINDABLE_BINDABLE_H_
#define BINDABLE_BINDABLE_BINDABLE_H_

#define BIND_PARENT_NAME(name) virtual void bindChild(BindableObject *child, std::string cname) { Bindable::bindChild(child, name); }

#include <string>
#include <map>
#include <set>

#include "Bindable.h"
namespace Bind
{
	/**
	 \class Bindable
	 \brief A base class meant to allow simple, automated binding between data structures.
	 
	 Consider the following scenario: Object MyDBRecord has fields NAME, EMAIL, and PHONE.
	 Object MyListItem has fields customer_name, customer_email, and customer_phone.
	 
	 To bind these together, you could manually set customer_name to NAME, customer_email
	 to EMAIL, and so-on, each time you bind the objects. Alternatively, you could hard-code
	 a data-binding function into MyListItem that would automatically bind all of the fields
	 to MyDBRecord. The first takes too much code, and the second is too specialized.
	 
	 Instead, using Bindable, you would, in MyListItem's constructor, call 
	 \c addProperty once for each property. Then, in the controlling object that
	 binds MyListItem to MyDBRecord, you would tell customer_name that it maps
	 to the field named NAME, customer_email that it maps to the field named
	 EMAIL, and customer_phone that it maps to the field named PHONE.
	 
	 Then, just call MyListItem->bind(MyDBRecord) to bind the list item to the 
	 record.
	 
	 The key use of this is automation. Sometimes, you may want to create a generic
	 object which has no knowledge of \e how to bind to any specific object, then
	 \e instruct it to do so at run-time.
	 
	 You can bind a BindableObject object to more than one other BindableObject object. For
	 record-keeping, theses binds are kept in memory, in order.
	 
	 When more than one binding is applied, each one \e overrides the previous for
	 any fields it has defined.
	 
	 */
	class BindableObject : public Bindable
	{
	public:
		/**
		 \fn BindableObject();
		 \brief Constructor.
		 */
		BindableObject();
		
		/**
		 \fn getTypeName();
		 \brief Returns the type name of this BindableObject object.
		 
		 It is recommended, although not required, that BindableObject implementers
		 implement this. If they do \e not, they should ensure that they are the
		 only ones who call bindChild (or they should override bindChild) so that
		 a good parent name is provided.
		 
		 \note A macro is provided to assist in overriding bindChild.
		 */
		virtual std::string getTypeName();
		
		
		/**
		\fn bind(BindableObject *bindTo, int depth = 0);
		\brief Binds this object to another object.
		
		 All managed properties, assuming they have selectors, will be bound to
		 their targets (should they exist).
		 
		 The \e depth parameter is used as a small performance booster; it tells
		 BindableObject ahead of time \e not to consider binding properties which are
		 obviously in no need of being re-bound It does so by determining the
		 depth - if any is set - of the current bind, and comparing it to
		 \e depth. If \e depth is lower (yet not equal to -1), then the binding
		 will be recalculated.
		 
		 It is still not recommended to bind entire trees of BindableObject objects
		 when not necessary, as it will waste \e lots of valuable CPU time.
		 (Obviously, when necessary is perfectly alright).
		*/
		virtual void bind(BindableObject *bindTo, int depth = 0);
		
		/**
		 \fn bind(BindableObject &bindTo, int depth);
		 \brief Alias to pointer-based bind (and thus inline).
		 */
		virtual void bind(BindableObject &bindTo, int depth) { bind(&bindTo, depth); }
		
		/**
		 \fn recalculateBindings(int recursionDepth = -1);
		 \brief Recalculates all bindings. Called when bindings change.
		 
		 This function loops through each level (likely from most close to least
		 close) and stops when all properties have been assigned, or when
		 maximumDepth has been reached.
		 
		 */
		virtual void recalculateBindings(int recursionDepth = -1);
		
		/**
		 \fn unbind(BindableObject *bindTo, bool recalculate = true);
		 \brief Unbinds from another object.
		 
		 This removes one bind from this object at the specified level.
		 
		 Currently calls recalculateBindings on finish. Should eventually do
		 something more elegant and nice. The recalculation is optional, as
		 during some processes (especially baking) it may be quite undesireable
		 to recalculate (bake, after all, means to leave things exactly as they
		 are, but with no bindings still active).
		 */
		virtual void unbind(BindableObject *bindTo, bool recalculate = true);
		
		/**
		 \fn get(std::string name, int recursionDepth = -1, bool skipSelf = false);
		 \brief Returns the property specified by \c name.
		 
		 If recursive is specified, the function will recursively search for
		 the field in all parents as well. If -1 is supplied, the limit will be
		 50 (for safety, the limit \e always maxes out at 50).
		 */
		virtual Bindable *get(std::string name, int recursionDepth = -1, bool skipSelf = false);
		
		/**
		 \fn set(Bindable &property, bool copy = true);
		 \brief Binds to a property.
		 */
		virtual void set(Bindable &property, bool copy = true);
		
		/**
		 \fn bake();
		 \brief Unbinds from everything, but keeps values if possible.
		 */
		virtual void bake();
		
		/**
		 \fn bindChild(BindableObject *bindable, std::string name = "");
		 \brief Binds an element considered to be a child of this element.
		 
		 This object will first attempt to bind the child object directly to
		 a property belonging to this object (including one sharing the same
		 name if auto-bind is enabled). The object will be bound to the found
		 property at level 0.
		 
		 After this (whether an appropriate bind target was found or not), the
		 object will be bound to all of the properties belonging to this object
		 at one level lower than whatever they are for this object.
		 
		 \note The mentioned "auto-bind" is currently always enabled; there is
		 no code to disallow it.
		 */
		void bindChild(BindableObject *bindable, std::string name = "");
		
		/**
		 \fn unbindChild(BindableObject *bindable);
		 \brief Unbinds an element considered to be a child of this element.
		 
		 Currently, this does the equivalent functionality of calling the child
		 element's unbind() for each item in this object's boundTo array.
		 
		 */
		void unbindChild(BindableObject *bindable);
		
		
		/**
		 \fn isBindableObject();
		 \brief Returns if this is (or represents) a BindableObject object.
		 
		 Accompanies getBindableObject().
		 */
		virtual bool isBindableObject();
		
		/**
		 \fn getBindableObject();
		 \brief Gets the BindableObject object that this represents.
		 */
		virtual BindableObject *getBindableObject();
		
		/**
		 \fn destroyed(Bindable *property);
		 \brief Called when a property we are listening to is being destroyed.
		 */
		virtual void destroyed(Bindable *property);
		
		using Bindable::operator=;
		
		virtual ~BindableObject();
	protected:
		/**
		 \fn addProperty(std::string name, Bindable *property);
		 \brief Adds the specified property to the managed properties dictionary.
		 */
		virtual void addProperty(std::string name, Bindable *property);
		
		/**
		 \fn addProperty(std::string name, Bindable &property);
		 \brief Adds the specified property to the managed properties dictionary.
		 
		 Inlined to reduce unnecessary operations.
		 */
		virtual void addProperty(std::string name, Bindable &property)
		{
			addProperty(name, &property);
		}
		
		/**
		 \fn removeProperty(std::string name);
		 \brief Removes the specified property from the managed properties
		 dictionary.
		 */
		virtual void removeProperty(std::string name);
		
		/**
		 \fn addParent(std::string name, BindableObject *parent);
		 \brief Adds a parent to the child. Protected because only the parent
		 should be able to call it.
		 */
		virtual void addParent(std::string name, BindableObject *parent);
		
		/**
		 \fn removeParent(BindableObject *parent);
		 \brief Removes a parent from the child. Protected because only the
		 parent should be allowed to call it.
		 
		 Note: this is a slow function, in theory. It iterates through \e each
		 parent, and checks if it is the parent to be removed.
		 */
		virtual void removeParent(BindableObject *parent);
		
	
		/**
		 \var properties;
		 \brief An map of properties belonging to the object. The map maps from
		 name to Bindable.
		 */
		std::map<std::string, Bindable *> properties;
		
		/**
		 \var propertySet;
		 \brief A map from property to how many times it is in the object.
		 
		 This allows for automatic deletion.
		 */
		std::map<Bindable *, int> propertySet;
		
		/**
		 \var autoDeleteProperties;
		 \brief If true, all properties will be automatically destroyed when
		 the object is.
		 */
		bool autoDeleteProperties;
		
	private:		
		/**
		 \var boundTo
		 \brief Contains the scoped list of objects bound to.
		 
		 The list is actually a list of lists. The main list is a stack of
		 bindings at different depths.
		 */
		std::vector<BindableObject *> boundTo;

		
		/**
		 \var children
		 \brief A list of objects bound using bindChild. This allows unbindChild
		 to be called on destruction.
		 
		 As it does not need a specific order, but \e will be used for searches
		 relatively often, it is a set.
		 */
		std::set<BindableObject *> children;
		
		/**
		 \var parents
		 \brief A multimap correlating string parent names to one or more parents.
		 
		 Parent selectors may warn if there is more than one parent (or special
		 parent selectors may attempt to select from the parents using some
		 kind of criteria).
		 
		 To access, other objects use the published getParent and getParentCount
		 functions. The getParent function takes an optional parent index;
		 NULL will be returned if it doesn't exist.
		 
		 Note: any child may only belong to any parent ONCE. It is the parent's
		 job to manage this, as it has a more efficiently accessible list of
		 children with which to reconcile and detect if it already has a child.
		 
		 If it does already have the item as a child, the existing connection
		 will be dropped.
		 */
		std::multimap<std::string, BindableObject *> parents;

	};

}


#endif